﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Net;
using System.Net.Mail;
using System.IO;
using System.Net.Mime;
using System.Security.Cryptography;

namespace SECSendEmailService
{
    partial class SendEmailService : ServiceBase
    {
        #region 服务控制
        //属性值


        SendEmailConf cg;
        //服务停止开关


        bool blStop = false;

        public SendEmailService()
        {
            // 该调用是 Windows.Forms 组件设计器所必需的。
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            // TODO: Add code here to start your service.
            // TODO: 在此处添加代码以启动服务 
            EventLog.WriteEntry("发邮件 服务启动!");
            File.AppendAllText("c:/1.txt", "发邮件 服务启动!\r\n");
            blStop = false;
            try
            {
                //读取xml文件
                string strPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "EmailConf.xml";
                //初始化邮件配置类
                cg = new SendEmailConf("SECTimeInfo", strPath);
                System.Threading.Thread td = new System.Threading.Thread(new System.Threading.ThreadStart(SendEmailRun));
                td.Start();
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(ex.Message);
                File.AppendAllText("c:/1.txt", "运行服务异常" +ex.Message + "\r\n");
            }
        }

        protected override void OnStop()
        {
            // TODO: Add code here to perform any tear-down necessary to stop your service.
            // TODO: 在此处添加代码以执行停止服务所需的关闭操作 
            EventLog.WriteEntry("发邮件 服务停止!");
            blStop = true;
        }
        #endregion

        #region 服务的执行主体方法

        private void SendEmailRun()
        {          
            string strcs = System.Configuration.ConfigurationManager.ConnectionStrings["CS"].ConnectionString;//连接数据库字符串
          //判断连接是否成功
            if (string.IsNullOrEmpty(strcs))
            {
                EventLog.WriteEntry("SendEmail的配置文件错误！");
                return;
            }
            File.AppendAllText("c:/1.txt", "数据库连接字符串为:"+strcs+"\r\n");
            //发送邮件          
            bool blExecute = false;       
            while (!blStop)//如果服务开启
            {                
                string strSysTime = DateTime.Now.ToString("yyyyMMdd HH:mm");
                #region 设置时间间隔
                //判断是否时间与设定的时间相同
                foreach (string strTime in cg.timeList)//cg邮件的配置类
                {
                    if (!(string.Equals(strSysTime, cg.lastDateTime)) && string.Equals(strSysTime, DateTime.Now.ToString("yyyyMMdd") + " " + strTime))
                    {
                        blExecute = true;
                        break;
                    }
                }
                if (blExecute)//时间匹配
                {
                    //保存上一次执行时间
                    cg.lastDateTime = strSysTime;
                    blExecute = false;//初始化发送条件 
                    File.AppendAllText("c:/1.txt", "时间匹配开始发送邮件!\r\n");
                    SendEmail(cg.Condition);//发送邮件内容
                } 
                #endregion
                else//不匹配休眠一分钟
                {                  
                    System.Threading.Thread.Sleep(60000);
                }
            }
        }
        #endregion

        #region 邮件发送_SendEmail
        private void SendEmail(string Condition)
        {           
            DB oledb = null;
            //暂时不需要附件
            //EmailLogFileAttachments 邮件附件表 是邮件和附件的中间表，FileAttachments 附件表 
            string sqlfile = @"select F.FileName,F.Path,F.FileData From dbo.EmailLogFileAttachments S left join FileAttachments F on S.FileAttachment_ID = F.ID where EmailLog_ID={0}";
            //关联邮件附件表和邮件表取出 附件的名字 附件的路径
            string mailGuid = "";      
            try
            {    //连接数据库      
                string strCnstring = GetCS();
                File.AppendAllText("c:/1.txt", "连接字符串为:" +strCnstring+ "\r\n");
                EventLog.WriteEntry(strCnstring+"test");
                oledb = new DB(strCnstring);
                //得到发送信息                       
                DataTable ContractDs = oledb.GetDataSetByProc("SP_Pub_SendMailList").Tables[0];
            
                if (ContractDs == null)
                {
                    EventLog.WriteEntry("数据获取失败！");
                }
                if (ContractDs.Rows.Count > 0)
                {
                    File.AppendAllText("c:/1.txt", "数据获取成功!行数为:" + ContractDs.Rows.Count + "\r\n");
                }
                else
                {
                    File.AppendAllText("c:/1.txt", "连接成功无数据要发送\r\n");
                }
                //发邮件
                string strTitle = "";//邮件的标题
                string strTo = "";//收件人
                string strContext = "";//
                string strCC = "";//
                List<string> strFiles = null;//文件对应的位置
                List<Attachment> ats = null;  //附件         
                if (ContractDs.Rows.Count > 0)
                {                    
                    for (int i = 0; i < ContractDs.Rows.Count; i++)
                    {
                        strFiles = new List<string>();                    
                        strTitle = "";
                        strTo = ContractDs.Rows[i]["Email"].ToString().Trim();//收件人
                        strContext = "";// ContractDs.Rows[i]["EmailContent"].ToString().Trim();
                        mailGuid = ContractDs.Rows[i]["EmailDetailLogID"].ToString();
                        StringBuilder MailBody = new StringBuilder();
                        MailBody.Append("<style>A:visited {	TEXT-DECORATION: none	}");
                        MailBody.Append("A:active  {	TEXT-DECORATION: none	}");
                        MailBody.Append("A:hover   {	TEXT-DECORATION: underline	}");
                        MailBody.Append("A:link 	  {	text-decoration: none;}");
                        MailBody.Append("A:visited {	text-decoration: none;}");
                        MailBody.Append("BODY   {	font-family: Arial, sans-serif; FONT-SIZE: 11pt;}");
                        MailBody.Append("TD	   {	font-family: Arial, sans-serif; FONT-SIZE: 11pt	}</style>");
                        MailBody.Append("<TABLE border=0 width='95%' align=center><TBODY><TR>");
                        MailBody.Append("<TD valign=middle align=top>");
                        MailBody.Append("<p>" + ContractDs.Rows[i]["EmailContent"].ToString().Trim() + "<p>");
                        MailBody.Append("</TD></TR>");
                        /*
                         * 2013-03-22 要求去掉邮件中的内外网入口，所以注释掉下面六行代码
                        if (cg.MailSignature.Trim().Length > 1)
                        {
                            MailBody.Append("<TR><TD valign=right align=top>");
                            MailBody.Append("<p>" + string.Format(cg.MailSignature, "http:\\" + cg.ServiceUrl, "http:\\" + cg.ServiceOutUrl, "</a>") + "<p>");
                            MailBody.Append("</TD></TR>");
                        }
                        */

                        if (cg.TestMailTo.Trim().Length > 1)//测试收件人
                        {
                            MailBody.Append("<TR><TD valign=right align=top>");
                            MailBody.Append("<p>以下内容仅在测试阶段显示：实际邮件接收人：" + ContractDs.Rows[i]["Email"].ToString().Trim() + "<br>抄送人：" + ContractDs.Rows[i]["CCAddress"].ToString().Trim() + "<p>");
                            MailBody.Append("</TD></TR>");
                            strTo = cg.TestMailTo;
                            //只有要测试邮件其间会如下处理
                            if (cg.TestMailCC.Trim().Length > 1)//测试发件人
                            {
                                strCC = cg.TestMailCC;
                            }
                        }
                        if (cg.TestMailTo.Trim().Length == 0 && cg.TestMailCC.Trim().Length == 0)
                        {
                            strCC = ContractDs.Rows[i]["CCAddress"].ToString().Trim();//得到发件人地址
                        }                                         
                        DataTable filedt = oledb.GetDataTable(string.Format(sqlfile, mailGuid));  //找到附件的名字和地址                    
                        if (filedt != null && filedt.Rows.Count > 0)
                        {
                            //找到附件
                            ats = new List<Attachment>();     
                            for (int l = 0; l < filedt.Rows.Count; l++)
                            {
                                //文件放在数据库中的话
                                if (cg.SaveFileInDataBase == true)
                                {
                                  //如果文件数据和文件的名字不为空的话
                                    if ((!string.IsNullOrEmpty(filedt.Rows[l]["FileData"].ToString().Trim())&&(!string.IsNullOrEmpty(filedt.Rows[l]["FileName"].ToString().Trim()))))
                                    {
                                        string strname = filedt.Rows[l]["FileName"].ToString().Trim();
                                        byte[] filedata =(byte[])(filedt.Rows[l]["FileData"]); //文件数据转化为字节                                  
                                        MemoryStream ms = new MemoryStream(filedata); //                                     
                                        Attachment at = new Attachment(ms, strname);                                  
                                        ats.Add(at);
                                    }                                 
                                }
                                else
                                {
                                    //不存在数据库的话就找到文件的地址
                                    if (!string.IsNullOrEmpty(filedt.Rows[l]["Path"].ToString().Trim()))
                                    {
                                        strFiles.Add(filedt.Rows[l]["Path"].ToString());
                                    }
                                }
                            }
                        }
                        else//没有附件
                        {
                            strFiles = null;              
                            ats= null;
                        }
                        MailBody.Append("</TBODY></TABLE><br>");
                        strContext = MailBody.ToString();//发送邮件字符串

                        if (cg.EmailTitlePrefix.Trim().Length > 1)//webservice的用户名
                        {
                            strTitle = cg.EmailTitlePrefix.Trim() + ContractDs.Rows[i]["Emailtitle"].ToString().Trim();//邮件的标题是服务的名字加上邮件标题
                        }                    
                        if (Send(strTitle, strContext, cg.mailFrom, strTo, cg.mailServer, cg.MailUserName, cg.MailPassWord, cg.MailFromShowName, strCC, strFiles,ats))
                        {                           

                            oledb.UpdateEmailStatus(mailGuid, 2); 
                        }
                        else
                        {                         
                            oledb.UpdateEmailStatus(mailGuid, 1);
                            File.AppendAllText("c:/1.txt", "发送失败跟新数据库123\r\n");
                        }

                    }
                }
            }
            catch (Exception err)
            {
                EventLog.WriteEntry("SECSendEmail服务", "SEC发送邮件服务异常" + err.Message, EventLogEntryType.Error);
                oledb.UpdateEmailStatus(mailGuid, 1);
                File.AppendAllText("c:/1.txt", "SEC发送邮件服务异常+"+err.Message+"\r\n");
            }
        }

        #region 真正的发邮件函数
        /// <summary>
        /// 真正的发送邮件
        /// </summary>
        /// <param name="mailTitle">邮件标题</param>
        /// <param name="mailBoby">邮件内容</param>
        /// <param name="mailFrom">发件人</param>
        /// <param name="mailTo">收件人</param>
        /// <param name="smtpServer">邮件服务器的名字</param>
        /// <param name="mailUserName">邮件服务器登入用户名</param>
        /// <param name="mailPassWord">邮件密码</param>
        /// <param name="displayName">显示的邮件用户名</param>
        /// <param name="CC">抄送人</param>
        /// <param name="_attachments">存储附件地址</param>
        /// <param name="_attachmentsFiledata">存储附件</param>
        /// <returns></returns>
        public bool Send(string mailTitle, string mailBoby, string mailFrom, string mailTo, string smtpServer, string mailUserName, string mailPassWord, string displayName, string CC, List<string> _attachments, List<Attachment> _attachmentsFiledata)
        {
            File.AppendAllText("c:/1.txt", "邮件标题：" + mailTitle + "\r\n");
            File.AppendAllText("c:/1.txt", "邮件内容：" + mailTitle + "\r\n");
            File.AppendAllText("c:/1.txt", "发件人：" + mailFrom + "\r\n");
            File.AppendAllText("c:/1.txt", "收件人：" + mailTo + "\r\n");
            File.AppendAllText("c:/1.txt", "邮件服务器的名字：" + smtpServer + "\r\n");
            File.AppendAllText("c:/1.txt", "邮件服务器登入用户名：" + mailUserName + "\r\n");
            File.AppendAllText("c:/1.txt", "邮件密码:" + mailPassWord + "\r\n");
            File.AppendAllText("c:/1.txt", "显示的邮件用户名：" + displayName + "\r\n");
            File.AppendAllText("c:/1.txt", "抄送人：" + CC + "\r\n");
            /*---------------------邮件附件异常----------------*/
            //File.AppendAllText("c:/1.txt", "存储附件地址" + _attachments.Count + "\r\n");
            //File.AppendAllText("c:/1.txt", "存储附件" + _attachmentsFiledata + "\r\n");
            bool Result = false;
            MailMessage mess = new MailMessage();
            mess.From = new MailAddress(mailFrom, displayName);
            mess.Subject = mailTitle;
            mess.Body = mailBoby;
            //mess.BodyEncoding = Encoding.Default; 可能会引起英文系统下的邮件体乱码，修改如下，待测试
            mess.BodyEncoding = Encoding.UTF8;
            mess.Priority = MailPriority.Normal;
            mess.IsBodyHtml = true;
            if (mailTo != "")
            {
                string[] _to = mailTo.Replace("；", ";").Replace(",", ";").Replace("，", ";").Split(';');
                foreach (var item in _to)
                {
                    if (item.Trim() != "")
                        mess.To.Add(item.Trim());
                }
            }

            if (CC != "")
            {
                string[] _cc = CC.Replace("；", ";").Replace(",", ";").Replace("，", ";").Split(';');
                foreach (var item in _cc)
                {
                    if (item.Trim() != "")
                        mess.CC.Add(item.Trim());
                }
            }

            #region 附件读取不成功异常
            try
            {
                //增加附件功能
                if (_attachments != null)
                {
                    if (!string.IsNullOrEmpty(cg.ServiceUser))
                    {
                        ImpersonationService iss = new ImpersonationService();
                        foreach (var item in _attachments)
                        {
                            try
                            {
                                iss.PerformImpersonatedTask(cg.ServiceUser, cg.ServiceDomain, cg.ServicePassword,
                                    2, 0,
                                    new Action(() =>
                                    {
                                        mess.Attachments.Add(new Attachment(item));
                                    }));
                            }
                            catch (Exception ex)
                            {
                                EventLog.WriteEntry("SECSendEmail 服务", "登录服务器读取文件失败：文件路径=" + item + " 错误信息：" + ex.Message);
                                File.AppendAllText("c:/1.txt", "发送异常" + ex.Message + "\r\n");
                            }
                        }
                    }
                    else
                    {
                        //EventLog.WriteEntry("SECSendEmail 服务", "没有配置远程账号");
                        foreach (var item in _attachments)
                        {
                            mess.Attachments.Add(new Attachment(item));
                        }
                    }
                }
                if (_attachmentsFiledata != null)
                {
                    foreach (var item in _attachmentsFiledata)
                    {
                        mess.Attachments.Add(item);
                    }
                }
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SECSendEmail 服务", "SEC发送邮件服务异常:读取附件失败；" + ex.Message, EventLogEntryType.Error);
                File.AppendAllText("c:/1.txt", "SEC发送邮件服务异常:读取附件失败；" + ex.Message + "\r\n");
            }
            #endregion

            #region 发送邮件异常部分
            try
            {
                SmtpClient sc = new SmtpClient(smtpServer);
                sc.Credentials = new System.Net.NetworkCredential(mailUserName, mailPassWord);
                sc.Send(mess);
                Result = true;
                EventLog.WriteEntry("SECSendEmail服务", "发送成功");
                File.AppendAllText("c:/1.txt", "SEC发送邮件Ok了\r\n");
            }
            catch (Exception err)
            {
                File.AppendAllText("c:/1.txt", "SEC发送邮件服务异常信息为:" + err.Message + "\r\n");
                EventLog.WriteEntry("SECSendEmail 服务", "SEC发送邮件服务异常aaa" + err.Message, EventLogEntryType.Error);
                return false;
            }
            #endregion

            return Result;
        } 
        #endregion
        
        #endregion

        #region 得到连接数据库的字符串
        private string GetCS()
        {
            string strcs = System.Configuration.ConfigurationManager.ConnectionStrings["CS"].ConnectionString;
            string[] ListCS = strcs.Split(';');
            string connectionstring = string.Empty;

            foreach (var item in ListCS)
            {
                string str = item;

                if (!string.IsNullOrEmpty(str))
                {
                    string TopStr = str.Substring(0, str.IndexOf("=")).Trim().ToLower();
                    if (TopStr.Equals("password") || TopStr.Equals("pwd"))
                    {
                        str = str.Replace(str.Substring(item.IndexOf("=") + 1), DecryptString(str.Substring(item.IndexOf("=") + 1)));
                    }
                    connectionstring = connectionstring + str + ";";
                }
            }

            return connectionstring;
        } 
        #endregion

        #region 加密处理
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="stringToDecrypt"></param>
        /// <returns></returns>
        private string DecryptString(string stringToDecrypt)
        {
            if (String.IsNullOrEmpty(stringToDecrypt))
            {
                return "";
            }

            string stringDecrypted = string.Empty;
            byte[] bytIn = Convert.FromBase64String(stringToDecrypt.Replace(" ", "+"));
            MemoryStream decryptStream = new MemoryStream();
            CryptoStream encStream = new CryptoStream(decryptStream, GenerateDESCryptoServiceProvider().CreateDecryptor(), CryptoStreamMode.Write);

            try
            {
                encStream.Write(bytIn, 0, bytIn.Length);
                encStream.FlushFinalBlock();
                stringDecrypted = Encoding.Default.GetString(decryptStream.ToArray());
            }
            catch
            {
                return "";
            }
            finally
            {
                decryptStream.Close();
                encStream.Close();
            }

            return stringDecrypted;
        }

        private DESCryptoServiceProvider GenerateDESCryptoServiceProvider()
        {
            DESCryptoServiceProvider dCrypter = new DESCryptoServiceProvider();
            string Key = cg.Encryptionkey;

            string sTemp;
            if (dCrypter.LegalKeySizes.Length > 0)
            {
                int moreSize = dCrypter.LegalKeySizes[0].MinSize;
                while (Key.Length > 7)
                {
                    Key = Key.Substring(0, 7);
                }
                sTemp = Key.PadRight(moreSize / 8, ' ');
            }
            else
            {
                sTemp = Key;
            }
            byte[] bytKey = ASCIIEncoding.ASCII.GetBytes(sTemp);

            dCrypter.Key = bytKey;
            dCrypter.IV = bytKey;

            return dCrypter;
        } 
        #endregion
              
    }
   
   
}
